package com.xiyuan.smartutils;

import com.xiyuan.smartutils.constants.SignConstants;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 随机相关工具类
 * <p>
 * 两种取随机数方式
 * 1、Random，
 * 2、SecureRandom
 * <p>
 * 两种操作方式
 * 1、支持传入内容取随机数
 * 2、提供默认的6种取随机数
 *
 * @version v1.0.1 @author xiyuan-lgz 2017-4-20 增加安全随机数和传入内容取随机数
 */

public final class Randoms implements SignConstants {
    
    private Randoms() {}
    
    /**
     * 指定数据源随机指定长度字符
     *
     * @param length 指定长度
     * @param source 指定数据源
     * @param random 最机器
     * @return 随机字符串
     */
    private static String random(int length, String source, Random random) {
        if (length <= 0 || source == null || source.isEmpty()) {return _EMPTY_;}
        
        char[] src = source.toCharArray();
        StringBuilder strb = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            strb.append(Arrays.shuffle(src)[0]);
        }
        
        return strb.toString();
    }
    
    
    /**
     * 随机生成一定长度的字符或数字
     *
     * @param length 长度
     * @param type   数据类型
     * @param random 随机器
     * @return 随机字符串
     */
    public static String random(int length, ResultType type, RandomType random) {
        if (length < 1 || type == null) {return _EMPTY_;}
        char[] source = Arrays.copyOf(type._letters_, type._letters_.length);
        if (length <= 0) {return _EMPTY_;}
        StringBuilder strb = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            strb.append(Arrays.shuffle(source, random.random)[0]);
        }
        return strb.toString();
    }
    
    /**
     * 随机生成一定长度的字符或数字
     *
     * @param length 随机个数
     * @param type   随机值类型
     * @return 随机值
     */
    public static String random(int length, ResultType type) {
        return random(length, type, RandomType.DEFAULT);
    }
    
    /**
     * 随机生成一定长度的字符或数字
     *
     * @param length 随机个数
     * @return 随机值
     */
    public static String random(int length) {
        return random(length, ResultType.DIGITS_LETTERS, RandomType.DEFAULT);
    }
    
    /**
     * 指定数据源 随机生成一定长度的字符数据
     *
     * @param length 随机个数
     * @param source 指定数据源
     * @return 随机值
     */
    public static String random(int length, String source) {
        return random(length, source, RandomType.DEFAULT.random);
    }
    
    /**
     * 32位通用唯一识别码
     */
    public static String uuid() {
        return UUID.randomUUID().toString().replaceAll("-", _EMPTY_);
    }
    
    /**
     * 32位通用唯一识别码
     *
     * @param separator 分割符
     * @return 32位通用唯一识别码
     */
    public static String uuid(String separator) {
        return UUID.randomUUID().toString().replaceAll("-", separator);
    }
    
    /**
     * 获得指定范围内的随机数
     *
     * @return 随机数
     * @see Random#nextInt(int)
     */
    public static int randomInt() {
        return getRandom().nextInt();
    }
    
    /**
     * 获得指定范围内的随机数 [0,limit)
     *
     * @param limit 限制随机数的范围，不包括这个数
     * @return 随机数
     * @see Random#nextInt(int)
     */
    public static int randomInt(int limit) {
        return getRandom().nextInt(limit);
    }
    
    /**
     * 获得指定范围内的随机数
     *
     * @param min 最小数（包含）
     * @param max 最大数（不包含）
     * @return 随机数
     */
    public static int randomInt(int min, int max) {
        return getRandom().nextInt(min, max);
    }
    
    /**
     * 获得指定范围内的随机数[min, max)
     *
     * @return 随机数
     * @see ThreadLocalRandom#nextLong()
     */
    public static long randomLong() {
        return getRandom().nextLong();
    }
    
    /**
     * 获得指定范围内的随机数[min, max)
     *
     * @param min 最小数（包含）
     * @param max 最大数（不包含）
     * @return 随机数
     * @see ThreadLocalRandom#nextLong(long, long)
     */
    public static long randomLong(long min, long max) {
        return getRandom().nextLong(min, max);
    }
    
    
    /**
     * 获得指定范围内的随机数
     *
     * @param min 最小数（包含）
     * @param max 最大数（不包含）
     * @return 随机数
     * @see ThreadLocalRandom#nextDouble(double, double)
     */
    public static double randomDouble(double min, double max) {
        return getRandom().nextDouble(min, max);
    }
    
    /**
     * 获得指定范围内的随机数
     *
     * @param min          最小数（包含）
     * @param max          最大数（不包含）
     * @param scale        保留小数位数
     * @param roundingMode 保留小数的模式 {@link RoundingMode}
     * @return 随机数
     */
    public static double randomDouble(double min, double max, int scale, RoundingMode roundingMode) {
        return Numbers.round(randomDouble(min, max), scale, roundingMode).doubleValue();
    }
    
    /**
     * 获得随机数[0, 1)
     *
     * @return 随机数
     * @see ThreadLocalRandom#nextDouble()
     */
    public static double randomDouble() {
        return getRandom().nextDouble();
    }
    
    /**
     * 获得指定范围内的随机数
     *
     * @param scale        保留小数位数
     * @param roundingMode 保留小数的模式 {@link RoundingMode}
     * @return 随机数
     */
    public static double randomDouble(int scale, RoundingMode roundingMode) {
        return Numbers.round(randomDouble(), scale, roundingMode).doubleValue();
    }
    
    /**
     * 获得指定范围内的随机数 [0,limit)
     *
     * @param limit 限制随机数的范围，不包括这个数
     * @return 随机数
     * @see ThreadLocalRandom#nextDouble(double)
     */
    public static double randomDouble(double limit) {
        return getRandom().nextDouble(limit);
    }
    
    /**
     * 获得指定范围内的随机数
     *
     * @param limit        限制随机数的范围，不包括这个数
     * @param scale        保留小数位数
     * @param roundingMode 保留小数的模式 {@link RoundingMode}
     * @return 随机数
     */
    public static double randomDouble(double limit, int scale, RoundingMode roundingMode) {
        return Numbers.round(randomDouble(limit), scale, roundingMode).doubleValue();
    }
    
    /**
     * 获得指定范围内的随机数[0, 1)
     *
     * @return 随机数
     */
    public static BigDecimal randomBigDecimal() {
        return Numbers.toBigDecimal(getRandom().nextDouble());
    }
    
    /**
     * 获得指定范围内的随机数 [0,limit)
     *
     * @param limit 最大数（不包含）
     * @return 随机数
     */
    public static BigDecimal randomBigDecimal(BigDecimal limit) {
        return Numbers.toBigDecimal(getRandom().nextDouble(limit.doubleValue()));
    }
    
    /**
     * 获得指定范围内的随机数
     *
     * @param min 最小数（包含）
     * @param max 最大数（不包含）
     * @return 随机数
     */
    public static BigDecimal randomBigDecimal(BigDecimal min, BigDecimal max) {
        return Numbers.toBigDecimal(getRandom().nextDouble(min.doubleValue(), max.doubleValue()));
    }
    
    /**
     * 随机bytes
     *
     * @param length 长度
     * @return bytes
     */
    public static byte[] randomBytes(int length) {
        byte[] bytes = new byte[length];
        getRandom().nextBytes(bytes);
        return bytes;
    }
    
    /**
     * 随机获得列表中的元素
     *
     * @param <T>  元素类型
     * @param list 列表
     * @return 随机元素
     */
    public static <T> T randomEle(List<T> list) {
        return randomEle(list, list.size());
    }
    
    /**
     * 随机获得列表中的元素
     *
     * @param <T>   元素类型
     * @param list  列表
     * @param limit 限制列表的前N项
     * @return 随机元素
     */
    public static <T> T randomEle(List<T> list, int limit) {
        if (list.size() < limit) {
            limit = list.size();
        }
        return list.get(randomInt(limit));
    }
    
    /**
     * 随机获得数组中的元素
     *
     * @param <T>   元素类型
     * @param array 列表
     * @return 随机元素
     */
    public static <T> T randomEle(T[] array) {
        return randomEle(array, array.length);
    }
    
    /**
     * 随机获得数组中的元素
     *
     * @param <T>   元素类型
     * @param array 列表
     * @param limit 限制列表的前N项
     * @return 随机元素
     */
    public static <T> T randomEle(T[] array, int limit) {
        if (array.length < limit) {
            limit = array.length;
        }
        return array[randomInt(limit)];
    }
    
    /**
     * 随机获得列表中的一定量元素
     *
     * @param <T>   元素类型
     * @param list  列表
     * @param count 随机取出的个数
     * @return 随机元素
     */
    public static <T> List<T> randomEles(List<T> list, int count) {
        final List<T> result = new ArrayList<>(count);
        int limit = list.size();
        while (result.size() < count) {
            result.add(randomEle(list, limit));
        }
        
        return result;
    }
    
    public static ThreadLocalRandom getRandom() {
        return ThreadLocalRandom.current();
    }
    
    /**
     * 随机数据源类型
     */
    public enum ResultType {
        /**
         * 大写字母
         */
        LETTERS_UPPER_CASE(_LETTERS_UPPERCASE_),
        /**
         * 小写字母
         */
        LETTERS_LOWER_CASE(_LETTERS_LOWERCASE_),
        
        /**
         * 大小写字母
         */
        LETTERS(_LETTERS_),
        
        /**
         * 数字
         */
        DIGITS(_DIGITS_),
        
        /**
         * 大写字母 + 数字
         */
        DIGITS_LETTERS_UPPER_CASE(_LETTERS_UPPERCASE_DIGITS_),
        /**
         * 小写字母+数字
         */
        DIGITS_LETTERS_LOWER_CASE(_LETTERS_LOWERCASE_DIGITS_),
        
        /**
         * 大小写字母+数字
         */
        DIGITS_LETTERS(_LETTERS_DIGITS_);
        private final String source;
        private final char[] _letters_;
        
        ResultType(String source) {
            this.source = source;
            this._letters_ = source.toCharArray();
        }
        
        public String getSource() {
            return source;
        }
    }
    
    
    /**
     * 随机器类型
     */
    public enum RandomType {
        /**
         * random
         */
        DEFAULT(new Random()),
        
        /**
         * secure_random
         */
        SECURE_RANDOM(new SecureRandom()),
        
        /**
         * ThreadLocalRandom
         */
        THREAD_LOCAL_RANDOM(getRandom());
        private final Random random;
        
        RandomType(Random random) {
            this.random = random;
        }
    }
}
